﻿using System;
using System.Data.SqlClient;
using Architecture.DataLayer.DbAccess;
using CommonComponents.Cache;
using CommonComponents.Configuration;
using CommonComponents.ExceptionHandling;
using LASLogic.BusinessLayer.DataModel;
using LASLogic.BusinessLayer.Utilities;

namespace LASLogic.DataLayer.Implementor
{
    internal class UserDataManager : DataManager<SqlDataReader, SqlParameter[], SqlParameter>
    {
        /// <summary>
        /// Se utiliza para todos los métodos que no son orientados a usuarios
        /// </summary>
        private readonly CacheManager _cacheManager;

        #region Public Methods

        /// <summary>
        /// Registra un nuevo usuario en la aplicación
        /// </summary>
        /// <param name="userId">Id del usuario registrado</param>
        /// <param name="companyId"></param>
        /// <param name="name">Nombre del usuario registrado</param>
        /// <param name="lastName1">Primer apellido de un usuario registrado</param>
        /// <param name="lastName2">Segundo apellido de un usuario registrado</param>
        /// <param name="login">Login del usuario registrado</param>
        /// <param name="password">Password del usuario registrado</param>
        /// <param name="email">Correo electrónico del usuario registrado</param>
        /// <returns>Devuelve si el usuario se registró o no, true para registrado, false para lo contrario</returns>
        public bool InsertNewUser(int userId, int companyId, string name, string lastName1, string lastName2,
                                  string login, string password, string email)
        {
            try
            {
                AppConfigReader appConfig = AppConfigReader.GetInstance();
                string spName = appConfig.GetProperty(Constants.SP_NAME_INSERT_USER);
                var parameters = new SqlParameter[7];
                parameters[0] = new SqlParameter(appConfig.GetProperty(Constants.SP_PARAM_INSERT_USER_NAME), name);
                parameters[1] = new SqlParameter(appConfig.GetProperty(Constants.SP_PARAM_INSERT_USER_LASTNAME1),
                                                 lastName1);
                parameters[2] = new SqlParameter(appConfig.GetProperty(Constants.SP_PARAM_INSERT_USER_LASTNAME2),
                                                 lastName2);
                parameters[3] = new SqlParameter(appConfig.GetProperty(Constants.SP_PARAM_INSERT_USER_COMPANY_ID),
                                                 companyId);
                parameters[4] = new SqlParameter(appConfig.GetProperty(Constants.SP_PARAM_INSERT_USER_LOGIN), login);
                parameters[5] = new SqlParameter(appConfig.GetProperty(Constants.SP_PARAM_INSERT_USER_EMAIL), email);
                parameters[6] = new SqlParameter(appConfig.GetProperty(Constants.SP_PARAM_INSERT_USER_PASSWORD),
                                                 password);
                var dataHelper = new SqlDataHelper(appConfig.GetConnectionString(Constants.DB_CONN));
                return GetResult(dataHelper.ExecuteStoredProcedure(spName, parameters), dataHelper);
            }
            catch (Exception ex)
            {
                ExceptionHandlingManager.ManageException(ex, ExceptionHandlingManager.ExceptionPolicyType.DataLayer);
                return false;
            }
        }


        /// <summary>
        /// Metodo que permite obtener un usuario a partir de un string de md5
        /// </summary>
        /// <param name="md5">String que contiene un md5 que representa al usuario</param>
        /// <returns>El usuario que se pidió</returns>
        public LasUser GetUser(string md5)
        {
            try
            {
                string key = Constants.SP_NAME_GET_USER + md5;
                if (_cacheManager.Contains(key))
                    return (LasUser) _cacheManager.Get(key);
                AppConfigReader appConfig = AppConfigReader.GetInstance();
                string spName = appConfig.GetProperty(Constants.SP_NAME_GET_USER);
                var parameters = new SqlParameter[1];
                parameters[0] = new SqlParameter(appConfig.GetProperty(Constants.SP_PARAM_GET_USER_MD5_VALUE), md5);
                var dataHelper = new SqlDataHelper(appConfig.GetConnectionString(Constants.DB_CONN));
                SqlDataReader result = dataHelper.ExecuteStoredProcedure(spName, parameters);
                LasUser user = null;
                while (result.Read())
                {
                    var userName = (string) result[appConfig.GetProperty(Constants.SP_OUTPUT_GET_USER_NAME)];
                    var userLogin = (string) result[appConfig.GetProperty(Constants.SP_OUTPUT_GET_USER_LOGIN)];
                    var userLastName1 =
                        (string) result[appConfig.GetProperty(Constants.SP_OUTPUT_GET_USER_LASTNAME1)];
                    var userLastName2 =
                        (string) result[appConfig.GetProperty(Constants.SP_OUTPUT_GET_USER_LASTNAME2)];
                    var userCompanyId =
                        (int) result[appConfig.GetProperty(Constants.SP_OUTPUT_GET_USER_COMPANY_ID)];
                    var userId =
                        (int) result[appConfig.GetProperty(Constants.SP_OUTPUT_GET_USER_USER_ID)];
                    user = new LasUser(userName, userLastName1, userLastName2, userId, userCompanyId, null,
                                       string.Empty, userLogin, string.Empty);
                }
                dataHelper.CloseReader();
                if (user != null)
                    _cacheManager.Add(key, user);

                return user;
            }
            catch (Exception ex)
            {
                ExceptionHandlingManager.ManageException(ex, ExceptionHandlingManager.ExceptionPolicyType.DataLayer);
                return null;
            }
        }

        /// <summary>
        /// Verifica si el login del usuario es valido, es decir no existen otros con el mismo login
        /// </summary>
        /// <param name="login">Usuario con su login</param>
        /// <returns>Bool indicando si es valido o no</returns>
        public bool ValidUser(string login)
        {
            try
            {
                AppConfigReader appConfig = AppConfigReader.GetInstance();
                string spName = appConfig.GetProperty(Constants.SP_NAME_VALID_USER);
                var parameters = new SqlParameter[1];
                parameters[0] = new SqlParameter(appConfig.GetProperty(Constants.SP_PARAM_VALID_USER_LOGIN), login);
                var dataHelper = new SqlDataHelper(appConfig.GetConnectionString(Constants.DB_CONN));
                return GetResult(dataHelper.ExecuteStoredProcedure(spName, parameters), dataHelper);
            }
            catch (Exception ex)
            {
                ExceptionHandlingManager.ManageException(ex, ExceptionHandlingManager.ExceptionPolicyType.DataLayer);
                return false;
            }
        }


        /// <summary>
        /// Metodo que permite obtener una dirección a partir de un identificador de usuario
        /// </summary>
        /// <returns>Direccion que se deseaba obtener</returns>
        public Address GetAddress(int userId)
        {
            try
            {
                AppConfigReader appConfig = AppConfigReader.GetInstance();
                string spName = appConfig.GetProperty(Constants.SP_NAME_GET_ADDRESS);
                var parameters = new SqlParameter[1];
                parameters[0] = new SqlParameter(appConfig.GetProperty(Constants.SP_PARAM_GET_ADDRESS_USER_ID), userId);
                var dataHelper = new SqlDataHelper(appConfig.GetConnectionString(Constants.DB_CONN));
                SqlDataReader result = dataHelper.ExecuteStoredProcedure(spName, parameters);
                Address address = null;
                while (result.Read())
                {
                    var city = (string) result[appConfig.GetProperty(Constants.SP_OUTPUT_GET_ADDRESS_CITY)];
                    var state = (string) result[appConfig.GetProperty(Constants.SP_OUTPUT_GET_ADDRESS_STATE)];
                    var country = (string) result[appConfig.GetProperty(Constants.SP_OUTPUT_GET_ADDRESS_COUNTRY)];
                    address = new Address(country, state, city);
                }
                dataHelper.CloseReader();
                return address;
            }
            catch (Exception ex)
            {
                ExceptionHandlingManager.ManageException(ex, ExceptionHandlingManager.ExceptionPolicyType.DataLayer);
                return null;
            }
        }

        #endregion

        #region Protected / Private Methods

        /// <summary>
        /// Dice si el Stored Procedure fue efectuado correctamente o no
        /// </summary>
        /// <param name="result">ResultSet que indica si fue efectuado correctamente o no</param>
        /// <param name="helper">DataHelper con que la operación fue efectuada</param>
        /// <returns>Verdadero si el SP se ejecutó bien, de lo contrario falso</returns>
        private static bool GetResult(SqlDataReader result, SqlDataHelper helper)
        {
            result.Read();
            bool actionPerformed =
                Convert.ToInt32(
                    result[AppConfigReader.GetInstance().GetProperty(Constants.SP_OUTPUT_ACTION_PERFORMED_RESULT)]) == 0
                    ? false
                    : true;
            helper.CloseConnection();
            return actionPerformed;
        }

        #endregion

        #region Singleton

        /// <summary>
        /// Única instancia de esta clase
        /// </summary>
        private static UserDataManager _instance;

        private UserDataManager()
        {
            _cacheManager = new CacheManager();
        }

        /// <summary>
        /// Método para implementar el patrón de Singleton
        /// </summary>
        /// <returns>Instancia única de la clase</returns>
        public static UserDataManager GetInstance()
        {
            if (_instance == null)
                _instance = new UserDataManager();
            return _instance;
        }

        #endregion
    }
}